/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.model;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.javaruntype.exceptions.TypeRecognitionException;
import org.javaruntype.type.Type;
import org.javaruntype.typedef.TypeDef;
import org.jfab.core.adapter.Adapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Provides additional functionality to a code generator type.
 */
public final class CGTypeUtilities
{
    /** Logger. */
    private static final Logger LOGGER = LoggerFactory
            .getLogger(CGTypeUtilities.class);

    /** Type factory. */
    private final TypeFactory _typeFactory;

    /** Code generator type adapter. */
    private final Adapter<CGType> _cgTypeAdapter;

    /**
     * Construct this object with the given parameters.
     * 
     * @param typeFactory Type factory.
     * @param cgTypeAdapter Code generator type adapter.
     */
    public CGTypeUtilities(final TypeFactory typeFactory,
            final Adapter<CGType> cgTypeAdapter)
    {
        _typeFactory = typeFactory;
        _cgTypeAdapter = cgTypeAdapter;
    }

    /**
     * @param cgType0 Code generator type.
     * @param cgType1 Code generator type.
     * 
     * @return true if the parameters are equal, false otherwise.
     */
    public boolean equals(final CGType cgType0, final CGType cgType1)
    {
        boolean answer;

        // Both null or same object.
        answer = cgType0 == null ? cgType1 == null : cgType0 == cgType1;

        if (!answer)
        {
            final String name0 = (cgType0 == null ? null : cgType0.getName());
            final String name1 = (cgType1 == null ? null : cgType1.getName());

            answer = (name0 == null ? name1 == null : name0.equals(name1));

            if (answer)
            {
                final String package0 = (cgType0 == null ? null : cgType0
                        .getPackageName());
                final String package1 = (cgType1 == null ? null : cgType1
                        .getPackageName());

                answer = (package0 == null ? package1 == null : package0
                        .equals(package1));

                if (answer)
                {
                    final List<CGType> params0 = (cgType0 == null ? null
                            : cgType0.getTypeParameters());
                    final List<CGType> params1 = (cgType1 == null ? null
                            : cgType1.getTypeParameters());

                    answer = (params0 == null ? params1 == null : params0
                            .equals(params1));
                }
            }
        }

        return answer;
    }

    /**
     * Gets the value of the type property.
     * 
     * @param cgType Code generator type.
     * 
     * @return type.
     */
    public final Type<?> getType(final CGType cgType)
    {
        Type<?> answer = null;

        try
        {
            answer = _typeFactory.createType(toNameString(cgType));
        }
        catch (final TypeRecognitionException e)
        {
            LOGGER.debug(e.getClass().getName() + ": " + e.getMessage());
        }

        return answer;
    }

    /**
     * Gets the value of the type definition property.
     * 
     * @param cgType Code generator type.
     * 
     * @return type definition.
     */
    public final TypeDef getTypeDef(final CGType cgType)
    {
        TypeDef answer = null;

        try
        {
            answer = _typeFactory.createTypeDef(toNameString(cgType));
        }
        catch (final TypeRecognitionException e)
        {
            LOGGER.debug(e.getClass().getName() + ": " + e.getMessage());
        }

        return answer;
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return true if this represents an array.
     */
    public final boolean isArray(final CGType cgType)
    {
        boolean answer = false;

        final Type<?> type = getType(cgType);

        if ((type != null) && (type.getRawClass() != null))
        {
            answer = type.getRawClass().isArray();
        }

        return answer;
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return true if this is in the default package.
     */
    public final boolean isInDefaultPackage(final CGType cgType)
    {
        return isInPackage(cgType, "");
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return true if this is in the java.lang package.
     */
    public final boolean isInJavaLangPackage(final CGType cgType)
    {
        return isInPackage(cgType, "java.lang");
    }

    /**
     * @param cgType Code generator type.
     * @param packageName Package name.
     * 
     * @return true if this is in the given package.
     */
    public final boolean isInPackage(final CGType cgType,
            final String packageName)
    {
        boolean answer = false;

        if (packageName != null)
        {
            final String myPackage = cgType.getPackageName();

            answer = packageName.equals(myPackage);
        }

        return answer;
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return true if this is a type parameter.
     */
    public final boolean isTypeParameter(final CGType cgType)
    {
        return (cgType.getName().length() == 1) && (getType(cgType) == null)
                && (getTypeDef(cgType) == null);
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return the fully qualified component name.
     */
    public final String toComponentNameString(final CGType cgType)
    {
        return stripGenericTypes(stripArrayBrackets(toNameString(cgType)));
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return the fully qualified name.
     */
    public final String toNameString(final CGType cgType)
    {
        return _cgTypeAdapter.marshal(cgType);
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return the simple component name.
     */
    public final String toSimpleComponentNameString(final CGType cgType)
    {
        return stripPackage(toComponentNameString(cgType));
    }

    /**
     * @param cgType Code generator type.
     * 
     * @return the simple name.
     */
    public final String toSimpleNameString(final CGType cgType)
    {
        String nameString = toNameString(cgType);

        String generics = "";
        final int index = nameString.indexOf('<');

        if (index >= 0)
        {
            generics = nameString.substring(index);
            nameString = nameString.substring(0, index);
        }

        return stripPackage(nameString) + generics;
    }

    /**
     * @param className Class name.
     * 
     * @return class name without generic types.
     */
    private String stripArrayBrackets(final String className)
    {
        String answer = className;

        if (StringUtils.isNotEmpty(answer))
        {
            final int index = answer.indexOf('[');

            if (index >= 0)
            {
                answer = answer.substring(0, index);
            }
        }

        return answer;
    }

    /**
     * @param className Class name.
     * 
     * @return class name without generic types.
     */
    private String stripGenericTypes(final String className)
    {
        String answer = className;

        if (StringUtils.isNotEmpty(answer))
        {
            final int index = answer.indexOf('<');

            if (index >= 0)
            {
                answer = answer.substring(0, index);
            }
        }

        return answer;
    }

    /**
     * @param className Fully qualified class name.
     * 
     * @return class name without a package.
     */
    private String stripPackage(final String className)
    {
        String answer = className;

        if (StringUtils.isNotEmpty(answer))
        {
            final int index = answer.lastIndexOf('.');

            if (index >= 0)
            {
                answer = answer.substring(index + 1);
            }
        }

        return answer;
    }
}
